package com.bytz.modules.cms.reseller.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.mail.MailUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.bytz.common.exception.BytzBootException;
import com.bytz.common.system.base.service.impl.AbstractCrudService;
import com.bytz.common.system.vo.LoginUser;
import com.bytz.common.util.RedisUtil;
import com.bytz.common.util.SecurityUtils;
import com.bytz.modules.cms.common.constants.CmsConstant;
import com.bytz.modules.cms.product.entity.Product;
import com.bytz.modules.cms.product.model.ProductModel;
import com.bytz.modules.cms.product.service.IAdminProductService;
import com.bytz.modules.cms.productLine.entity.ProductLine;
import com.bytz.modules.cms.productLine.entity.ProductLineLevel;
import com.bytz.modules.cms.productLine.service.IProductLineLevelService;
import com.bytz.modules.cms.productLine.service.IProductLineService;
import com.bytz.modules.cms.reseller.constant.ResellerConstant;
import com.bytz.modules.cms.reseller.entity.Reseller;
import com.bytz.modules.cms.reseller.entity.ResellerLevel;
import com.bytz.modules.cms.reseller.entity.ResellerSales;
import com.bytz.modules.cms.reseller.mapper.ResellerMapper;
import com.bytz.modules.cms.reseller.model.ResellerLineInfo;
import com.bytz.modules.cms.reseller.model.ResellerModel;
import com.bytz.modules.cms.reseller.model.ResellerPriceExcel;
import com.bytz.modules.cms.reseller.service.*;
import com.bytz.modules.cms.reseller.util.ResellerSecurityUtils;
import com.bytz.modules.cms.user.entity.CmsSysUser;
import com.bytz.modules.cms.user.service.ICmsSysUserService;
import com.bytz.modules.system.dto.EmailDto;
import com.bytz.modules.system.entity.SysUser;
import com.bytz.modules.system.service.INotifyService;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.OutputStream;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class AdminResellerServiceImpl extends AbstractCrudService<ResellerMapper, Reseller, ResellerModel> implements IAdminResellerService {

    @Autowired
    private IResellerEndUserService resellerEndUserService;

    @Autowired
    private IResellerService resellerService;

    @Autowired
    private IProductLineService productLineService;

    @Autowired
    private IProductLineLevelService productLineLevelService;

    @Autowired
    private IResellerLevelService resellerLevelService;

    @Autowired
    private IResellerLevelChangeService resellerLevelChangeService;

    @Autowired
    private IEndUserService endUserService;

    @Autowired
    private IAdminProductService productService;

    @Autowired
    private ICmsSysUserService userService;

    @Autowired
    private IResellerSalesService resellerSalesService;

    @Autowired
    private INotifyService notifyService;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean resetPassword(String id) {

        Reseller reseller = getById(id);

        return resetPassword(reseller);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean resetPassword(Reseller reseller) {

        String password = RandomUtil.randomString(12);

        boolean success = resellerService.updatePassword(reseller.getId(), reseller.getUsername(), ResellerSecurityUtils.encryptPassword(password));

        if (StringUtils.isNotBlank(reseller.getEmail())) {
            EmailDto emailDto = new EmailDto();
            emailDto.setTos(Sets.newHashSet(reseller.getEmail()));
            emailDto.setSubject(ResellerConstant.Email.RESET_PASS_SUBJECT);
            emailDto.setContent(String.format(ResellerConstant.Email.RESET_PASS_CONTEXT, password));
            try {
                notifyService.sendEmail(emailDto);
            } catch (Exception e) {
                throw new BytzBootException("发送重置密码邮件失败", e);
            }
        } else {
            throw new BytzBootException("用户邮箱为空，无法发送密码");
        }

        return success;
    }

    @Override
    public void generatePriceList(HttpServletResponse response, Wrapper<Reseller> wrapper) {

        List<Reseller> resellers = resellerService.list(wrapper);

        resellers.forEach(reseller -> {
            if (StringUtils.isBlank(reseller.getEmail())) {
                throw new BytzBootException("经销商" + reseller.getResellerName() + "的邮箱为空，无法发送价格表");
            }
        });

        List<Product> products = productService.getPriceNotNull();

        Map<String, File> emailFileMap = resellers.stream().collect(Collectors.toMap(Reseller::getEmail, reseller -> {

            List<ProductModel> productModels = productService.getPriceByResellerId(reseller.getId(), products);
            List<ResellerPriceExcel> result = BeanUtil.copyToList(productModels, ResellerPriceExcel.class).stream()
                    .filter(resellerPriceExcel -> resellerPriceExcel.getCalculatePrice() != null)
                    .collect(Collectors.toList());

            result.forEach(resellerPriceExcel -> {
                resellerPriceExcel.setCalculatePrice(resellerPriceExcel.getCalculatePrice().setScale(2, RoundingMode.HALF_UP));
            });

            File file = new File(String.format(ResellerConstant.FileName.PRICE_LIST, reseller.getResellerName()));

            try (OutputStream os = Files.newOutputStream(file.toPath())) {

                ExcelWriter excelWriter = EasyExcel.write(os, ResellerPriceExcel.class).build();
                WriteSheet writeSheet = EasyExcel.writerSheet().build();

                excelWriter.write(result, writeSheet);
                excelWriter.finish();

                os.flush();

                return file;
            } catch (Exception e) {

                System.gc();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ignore) {
                }

                file.delete();

                throw new BytzBootException(e);
            }
        }));

        emailFileMap.forEach((email, file) -> {
            MailUtil.send(
                    email,
                    ResellerConstant.Email.PRICE_LIST_SUBJECT,
                    ResellerConstant.Email.PRICE_LIST_CONTEXT,
                    false,
                    file
            );
        });

        System.gc();
        try {
            Thread.sleep(500);
        } catch (InterruptedException ignore) {
        }

        emailFileMap.values().forEach(File::delete);
    }

    @Override
    public Boolean changeStatus(String id, String status) {
        Reseller reseller = getById(id);
        boolean update = lambdaUpdate()
                .eq(Reseller::getId, id)
                .set(Reseller::getStatus, status)
                .update();
        if (update) {
            redisUtil.del(ResellerConstant.ResellerSecurity.SECURITY_CACHE_PREFIX + ":" + reseller.getUsername());
            redisUtil.del(ResellerConstant.ResellerSecurity.CACHE_PREFIX + ":" + reseller.getUsername());
        }
        return update;
    }

    @Override
    public ResellerModel getModelById(String id) {

        ResellerModel model = BeanUtil.copyProperties(getById(id), ResellerModel.class);
        model.setEndUsers(endUserService.getByResellerId(id, ResellerConstant.ResellerEndUserType.SOLD_TO));

        List<ProductLine> lines = productLineService.list();
        List<ResellerLevel> resellerLevels = resellerLevelService.getByResellerId(id);

        // 销售id - 经销商销售关联entity
        Map<String, ResellerSales> resellerSalesMap = resellerSalesService.getByResellerId(id)
                .stream().collect(Collectors.toMap(ResellerSales::getSalesId, t -> t));

        // 产品线id - 产品先对应的经销商等级
        Map<String, ResellerLevel> productLineLevelGroup = resellerLevels.stream().collect(Collectors.toMap(ResellerLevel::getProductLineId, t -> t));

        Map<String, ProductLineLevel> levelMap = productLineLevelService.getByIds(resellerLevels.stream().map(ResellerLevel::getProductLineLevelId).collect(Collectors.toSet()))
                .stream().collect(Collectors.toMap(ProductLineLevel::getId, t -> t));

        Map<String, List<CmsSysUser>> saleMap = userService.getByIds(resellerSalesMap.keySet())
                .stream().collect(Collectors.groupingBy(SysUser::getCurrentDeptId));

        model.setLineInfos(new ArrayList<>());
        lines.forEach(line -> {
            ResellerLevel resellerLevel = productLineLevelGroup.get(line.getId());

            // 如果对应产品线没有等级，前端就不显示这条生产线了
            if (resellerLevel == null) {
                return;
            }

            ResellerLineInfo resellerLineInfo = new ResellerLineInfo();
            model.getLineInfos().add(resellerLineInfo);

            // 该产品线基础信息
            resellerLineInfo.setProductLine(line);

            // 该产品线的等级
            String levelId = resellerLevel.getProductLineLevelId();
            resellerLineInfo.setResellerLevel(levelMap.get(levelId));

            // 判断是否能编辑
            LoginUser loginUser = SecurityUtils.getLoginUser();
            boolean sameLine = StringUtils.equals(loginUser.getDeptId(), line.getDeptId());
            resellerLineInfo.setCanEdit(sameLine ||
                    SecurityUtils.hasRole(CmsConstant.Role.ADMIN) ||
                    SecurityUtils.hasRole(CmsConstant.Role.ROOT));

            // 该产品线的销售信息
            List<CmsSysUser> users = saleMap.get(line.getDeptId());
            if (CollectionUtil.isEmpty(users)) {
                return;
            }
            List<ResellerSales> lineSales = users.stream()
                    .map(user -> resellerSalesMap.get(user.getId())).collect(Collectors.toList());
            resellerLineInfo.setSaleList(lineSales);
        });

        return model;
    }

    @Override
    public void importExcel(List<Reseller> list) {

        Set<String> usernames = list.stream().map(Reseller::getUsername).collect(Collectors.toSet());
        if (usernames.size() != list.size()) {
            throw new BytzBootException("用户名不能重复");
        }

        if (CollectionUtil.isEmpty(usernames)) {
            return;
        }

        List<String> repetitionUsername = lambdaQuery().in(Reseller::getUsername, usernames).list()
                .stream().map(Reseller::getUsername).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(repetitionUsername)) {
            throw new BytzBootException("用户【" + StringUtils.join(repetitionUsername, ",") + "】已存在");
        }

        list.forEach(reseller -> {

            reseller.setStatus(ResellerConstant.ResellerStatus.ENABLE);

            if (StringUtils.isBlank(reseller.getEmail())) {
                throw new BytzBootException("用户邮箱为空，无法发送密码");
            }
        });

        saveBatch(list);

        list.forEach(this::resetPassword);
    }

    @Override
    public List<Reseller> findByResellerName(Collection<String> resellerNames) {
        return this.lambdaQuery().in(Reseller::getResellerName, resellerNames).list();
    }

    @Override
    public List<Reseller> searchByName(String name) {
        return lambdaQuery().like(Reseller::getResellerName, name).list();
    }

    @Override
    protected ResellerModel insertSaveObject(ResellerModel saveRo) {
        if (saveRo.getStatus() == null) {
            saveRo.setStatus(ResellerConstant.ResellerStatus.ENABLE);
        }
        setLevelNames(saveRo);
        save(saveRo);
        resetPassword(saveRo);
        return saveRo;
    }

    @Override
    @CacheEvict(cacheNames = {ResellerConstant.ResellerSecurity.SECURITY_CACHE_PREFIX, ResellerConstant.ResellerSecurity.CACHE_PREFIX}, key = "#saveRo.username")
    public ResellerModel saveOrUpdateSaveObject(ResellerModel saveRo, boolean checkAuth) {
        // 更新经销商时清除对应经销商缓存
        return super.saveOrUpdateSaveObject(saveRo, checkAuth);
    }

    @Override
    protected ResellerModel updateSaveObject(ResellerModel saveRo) {
        setLevelNames(saveRo);
        updateById(saveRo);
        return saveRo;
    }

    /**
     * 查找对应的等级名称，填入resellerLevel对象的levelName字段，顿号分隔填入reseller对象的levels字段
     */
    private void setLevelNames(ResellerModel saveRo) {

        List<String> levelIds = saveRo.getLineInfos().stream().map(info -> info.getResellerLevel().getId()).collect(Collectors.toList());

        Map<String, String> lineIdLevelNameMap = productLineLevelService.getByIds(levelIds).stream()
                .collect(Collectors.toMap(ProductLineLevel::getProductLineId, ProductLineLevel::getLevelName));

        String levels = saveRo.getLineInfos().stream().map(lineInfo -> {
            String levelName = lineIdLevelNameMap.get(lineInfo.getProductLine().getId());
            lineInfo.getResellerLevel().setLevelName(levelName);
            return levelName;
        }).collect(Collectors.joining("、"));

        saveRo.setLevels(levels);
    }

    @Override
    protected ResellerModel saveOthers(ResellerModel saveRo) {
        endUserService.saveSoldToByResellerModel(saveRo);
        resellerSalesService.saveSalesByResellerModel(saveRo);
        resellerLevelService.saveLevelByResellerModel(saveRo);
        return saveRo;
    }

    @Override
    @CacheEvict(cacheNames = {ResellerConstant.ResellerSecurity.SECURITY_CACHE_PREFIX, ResellerConstant.ResellerSecurity.CACHE_PREFIX}, allEntries = true)
    public boolean checkAndRemoveById(String id) {
        // 删除经销商时清除全部经销商缓存
        return super.checkAndRemoveById(id);
    }

    @Override
    protected boolean deleteEntity(Reseller entity) {
        return removeById(entity.getId());
    }

    @Override
    protected void deleteOthers(Reseller entity) {
        resellerEndUserService.deleteByResellerId(entity.getId());
    }
}
